package modelcontroller;

import java.util.HashMap;
import java.util.Observable;
import javax.swing.DefaultListModel;

/**
 *
 * @author Mariam
 */
public class ChatModel extends Observable {

    private User localUser;
    private boolean localUserIsConnected; // state
    private DefaultListModel connectedUsers; // localUser not in the list
    private DefaultListModel conversations;
    private HashMap<User, Conversation> hashmapConv;
    private HashMap<String, User> hashmapUsers;

    /**
     * Constructs a new model that is initially empty
     */
    public ChatModel() {
        this.connectedUsers = new DefaultListModel();
        this.hashmapUsers = new HashMap<String, User>();
        this.localUserIsConnected = false;
        this.conversations = new DefaultListModel();
        this.hashmapConv = new HashMap<User, Conversation>();
    }

    /**
     * Sets the local user (when he gets connected to the Chat System)
     *
     * @param localUser the local user
     * @see User
     */
    public void setLocalUser(User localUser) {
        this.localUser = localUser;
    }

    /**
     * Sets the state of the local user
     *
     * @param connected true to change the user's state to connected or false to
     * change it to disconnected
     */
    public void setLocalUserIsConnected(boolean connected) {
        this.localUserIsConnected = connected;
        this.setChanged();
        notifyObservers(ValueUpdated.STATE);
    }

    /**
     * Gets the local user
     *
     * @return the local user associated to the model
     * @see User
     */
    public User getLocalUser() {
        return localUser;
    }

    /**
     * Gets the status of the local user
     *
     * @return true if the local user is connected, false if he is disconnected
     */
    public boolean getLocalUserIsConnected() {
        return localUserIsConnected;
    }

    /**
     * Gets the list of the connected users (the local user is not part of the
     * list)
     *
     * @return list of all the remote connected users
     */
    public DefaultListModel getConnectedUsers() {
        return connectedUsers;
    }

    /**
     * Gets the list of the conversations between the local user and other users
     *
     * @return the list of the conversations of the local users
     */
    public DefaultListModel getConversations() {
        return conversations;
    }

    /**
     * Gets the hashmap related to the conversations list
     * <p>
     * The key is the remote user associated to a conversation (which stands for
     * the value)
     *
     * @return the hashmap of the conversations
     */
    public HashMap<User, Conversation> getHashmapConv() {
        return hashmapConv;
    }

    /**
     * Gets the hashmap related to the connected users list
     *
     * @return the hashmap of the connected users
     */
    public HashMap<String, User> getHashmapUsers() {
        return hashmapUsers;
    }

    /**
     * Adds a new connected remote user into the model
     * <p>
     * It changes the list and hashmap of the connected users
     *
     * @param newUser the new user to add to the model
     */
    public void addUser(User newUser) {
        if (!hashmapUsers.containsKey(newUser.getUsername())) {
            connectedUsers.addElement(newUser);
            hashmapUsers.put(newUser.getUsername(), newUser);
            this.setChanged();
            notifyObservers(ValueUpdated.USERSLIST);
        }
        Conversation c = hashmapConv.get(newUser);
        if (c != null) {
            c.addMessage(newUser.getUsername() + " : ---- IS BACK ! ----");
            updateConversations();
        }
    }

    /**
     * Removes the specified user from the model
     *
     * @param exitingUser the user to remove from the model because he is no
     * longer connected to the Chat System
     */
    public void removeUser(User exitingUser) {
        if (hashmapUsers.get(exitingUser.getUsername()) != null) {
            connectedUsers.removeElement(exitingUser);
            hashmapUsers.remove(exitingUser.getUsername());
            this.setChanged();
            notifyObservers(ValueUpdated.USERSLIST);
        }
    }

    /**
     * Notifies the observers that the conversations list has changed
     */
    protected void updateConversations() {
        this.setChanged();
        notifyObservers(ValueUpdated.CONVERSATIONSLIST);
    }

    /**
     * Removes the conversation from the model
     *
     * @param conversation
     */
    public void removeConversation(Conversation conversation) {
        conversations.removeElement(conversation);
        hashmapConv.remove(conversation.getRemoteUser());
        this.setChanged();
        notifyObservers(ValueUpdated.CONVERSATIONSLIST);
    }

    /**
     * Creates a new conversation with the remote user
     *
     * @param remoteUser the remote user associated to the new conversation
     * @return the conversation that has been created
     */
    public Conversation createConversation(User remoteUser) {
        Conversation c = new Conversation(remoteUser);
        conversations.addElement(c);
        hashmapConv.put(remoteUser, c);
        updateConversations();
        return c;
    }

    /**
     * Enumeration of the parts of the model which can be modified.
     * <p>
     * When the model is updated, the observers of the model are notified that
     * the specified part has changed
     */
    public enum ValueUpdated {

        /**
         * The state of the local user
         */
        STATE,
        /**
         * The list of the connected remote users
         */
        USERSLIST,
        /**
         * The list of the conversations between the local users and other users
         */
        CONVERSATIONSLIST;
    }
}
